{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "animated-beam",
"type": "registry:component",
"dependencies": [],
"devDependencies": [],
"registryDependencies": [],
"files": [
{
"path": "./components/ui/animated-beam.tsx",
"content": "'use client';\r\n\r\nimport { cn } from '@/lib/utils';\r\nimport { motion } from 'motion/react';\r\nimport { forwardRef, RefObject, useEffect, useId, useState } from 'react';\r\n\r\nexport const Icons = {\r\n user: () => (\r\n \r\n \r\n \r\n \r\n ),\r\n claude: () => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ),\r\n framer: () => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ),\r\n logo: () => (\r\n \r\n \r\n \r\n ),\r\n typescript: () => (\r\n \r\n \r\n \r\n \r\n ),\r\n nextjs: () => (\r\n \r\n \r\n \r\n \r\n ),\r\n tailwindcss: () => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ),\r\n gsap: () => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ),\r\n reactjs: () => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n ),\r\n};\r\n\r\nexport interface AnimatedBeamProps {\r\n className?: string;\r\n containerRef: RefObject; // Container ref\r\n fromRef: RefObject;\r\n toRef: RefObject;\r\n curvature?: number;\r\n reverse?: boolean;\r\n pathColor?: string;\r\n pathWidth?: number;\r\n pathOpacity?: number;\r\n gradientStartColor?: string;\r\n gradientStopColor?: string;\r\n delay?: number;\r\n duration?: number;\r\n startXOffset?: number;\r\n startYOffset?: number;\r\n endXOffset?: number;\r\n endYOffset?: number;\r\n dotted?: boolean;\r\n dotSpacing?: number;\r\n}\r\n\r\nconst AnimatedBeam: React.FC = ({\r\n className,\r\n containerRef,\r\n fromRef,\r\n toRef,\r\n curvature = 0,\r\n reverse = false, // Include the reverse prop\r\n duration = Math.random() * 3 + 4,\r\n delay = 0,\r\n pathColor = 'gray',\r\n pathWidth = 2,\r\n pathOpacity = 0.2,\r\n gradientStartColor = '#4d40ff',\r\n gradientStopColor = '#4043ff',\r\n startXOffset = 0,\r\n startYOffset = 0,\r\n endXOffset = 0,\r\n endYOffset = 0,\r\n dotted = false,\r\n dotSpacing = 6,\r\n}) => {\r\n const id = useId();\r\n const [pathD, setPathD] = useState('');\r\n\r\n const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 });\r\n const strokeDasharray = dotted ? `${dotSpacing} ${dotSpacing}` : 'none';\r\n // Calculate the gradient coordinates based on the reverse prop\r\n const gradientCoordinates = reverse\r\n ? {\r\n x1: ['90%', '-10%'],\r\n x2: ['100%', '0%'],\r\n y1: ['0%', '0%'],\r\n y2: ['0%', '0%'],\r\n }\r\n : {\r\n x1: ['10%', '110%'],\r\n x2: ['0%', '100%'],\r\n y1: ['0%', '0%'],\r\n y2: ['0%', '0%'],\r\n };\r\n\r\n useEffect(() => {\r\n const updatePath = () => {\r\n if (containerRef.current && fromRef.current && toRef.current) {\r\n const containerRect = containerRef.current.getBoundingClientRect();\r\n const rectA = fromRef.current.getBoundingClientRect();\r\n const rectB = toRef.current.getBoundingClientRect();\r\n\r\n const svgWidth = containerRect.width;\r\n const svgHeight = containerRect.height;\r\n setSvgDimensions({ width: svgWidth, height: svgHeight });\r\n\r\n const startX =\r\n rectA.left - containerRect.left + rectA.width / 2 + startXOffset;\r\n const startY =\r\n rectA.top - containerRect.top + rectA.height / 2 + startYOffset;\r\n const endX =\r\n rectB.left - containerRect.left + rectB.width / 2 + endXOffset;\r\n const endY =\r\n rectB.top - containerRect.top + rectB.height / 2 + endYOffset;\r\n\r\n const controlY = startY - curvature;\r\n const d = `M ${startX},${startY} Q ${\r\n (startX + endX) / 2\r\n },${controlY} ${endX},${endY}`;\r\n setPathD(d);\r\n }\r\n };\r\n\r\n // Initialize ResizeObserver\r\n const resizeObserver = new ResizeObserver((entries) => {\r\n // For all entries, recalculate the path\r\n for (let entry of entries) {\r\n updatePath();\r\n }\r\n });\r\n\r\n // Observe the container element\r\n if (containerRef.current) {\r\n resizeObserver.observe(containerRef.current);\r\n }\r\n\r\n // Call the updatePath initially to set the initial path\r\n updatePath();\r\n\r\n // Clean up the observer on component unmount\r\n return () => {\r\n resizeObserver.disconnect();\r\n };\r\n }, [\r\n containerRef,\r\n fromRef,\r\n toRef,\r\n curvature,\r\n startXOffset,\r\n startYOffset,\r\n endXOffset,\r\n endYOffset,\r\n ]);\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n// Add this line after your component definition\r\nAnimatedBeam.displayName = 'AnimatedBeam';\r\n\r\n// Then export the component\r\nexport { AnimatedBeam };\r\nconst Circle = forwardRef<\r\n HTMLDivElement,\r\n { className?: string; children?: React.ReactNode }\r\n>(({ className, children }, ref) => {\r\n return (\r\n \r\n {children}\r\n
\r\n );\r\n});\r\nCircle.displayName = 'Circle';\r\n\r\n// Then export the Circle component\r\nexport { Circle };\r\n",
"type": "registry:component"
}
]
}